//16位代码
__asm__(".code16gcc");

#include "loader.h"

boot_info_t boot_info;

//设置全局段描述符表
uint16_t gdt_table[][4] = {
    {0, 0, 0, 0},                           //空描述符
    {0xFFFF, 0x0000, 0x9A00, 0x00CF},       
    {0xFFFF, 0x0000, 0x9200, 0x00CF},       
};


//无符号数转换为16进制字符串
static void uintToHex(uint32_t num, char* str, int len) 
{
    int index = len - 1; // 从字符串的末尾开始填充
    char *hex = "0123456789ABCDEF";

    while (index >= 0) {
        // 获取最低的4位，并转换为相应的16进制字符
        str[index] = hex[(num & 0xF)];
        num >>= 4; // 右移4位
        index--;
    }

    str[len - 1] = '\0'; // 确保字符串以空字符终止
}

//使用内联汇编显示文字
static void print_msg(const char* msg)
{
    char c;
    while((c=*msg++) != '\0')
    {
        //内联汇编
        __asm__ __volatile__(             //__asm__ __volatile__ 避免编译器优化
            "mov $0xe, %%ah\n\t"
            "mov %[ch] ,%%al\n\t"
            "int $0x10" ::[ch]"r"(c)      //r 表示随机寄存器        
        );
    }
}

//检测内存容量
static void  detect_memory(void) 
{
	uint32_t contID = 0;
	SMAP_entry_t smap_entry;
	int signature, bytes;

    print_msg("detect memory......\n\r");

	// 初次：EDX=0x534D4150,EAX=0xE820,ECX=24,INT 0x15, EBX=0（初次）
	// 后续：EAX=0xE820,ECX=24,
	// 结束判断：EBX=0
	boot_info.ram_region_count = 0;
	for (int i = 0; i < RAM_REGION_MAX; i++) 
    {
		SMAP_entry_t * entry = &smap_entry;
        //内联汇编

		__asm__ __volatile__(
            "int  $0x15"
			: "=a"(signature), "=c"(bytes), "=b"(contID)     
			: "a"(0xE820), "b"(contID), "c"(24), "d"(0x534D4150), "D"(entry));         
		//"=a"(signature)：EAX->signature。"=c"(bytes)：ECX->bytes。"=b"(contID)：EBX->contID。
        //"a"(0xE820)：0xE820->EAX。调用E820内存映射函数 "b"(contID)：contID->EBX。"c"(24)：24->ECX。"d"(0x534D4150)：0x534D4150->EDX  "D"(entry) :entry_>EDI

        if (signature != 0x534D4150) 
        {
            print_msg("failed.\r\n");
			return;
		}

		// todo: 20字节
		if (bytes > 20 && (entry->ACPI & 0x0001) == 0)
        {
			continue;
		}

        // 保存RAM信息，只取32位
        if (entry->Type == 1) 
        {
            boot_info.ram_region[boot_info.ram_region_count].start = entry->BaseL;
            boot_info.ram_region[boot_info.ram_region_count].size =  entry->LengthL;
            boot_info.ram_region_count++;
        }

		if (contID == 0) 
        {
			break;
		}
	}
    //打印可用内存
    print_msg("usable memory:\r\n");
    for(int i=0;i<boot_info.ram_region_count;i++)
    {
        char str[8]={0};
        uintToHex(boot_info.ram_region[i].start,str,8);
        print_msg("start: 0x");
        print_msg(str);
        print_msg("  ");
        uintToHex(boot_info.ram_region[i].size,str,8);
        print_msg("size: 0x");
        print_msg(str);
        print_msg("\r\n");
    }
    print_msg("detection completed.\r\n");
}

//进入保护模式
static void protect_entry()
{
    cli();   //关闭中断
    
    //打开A20地址线以访问4GB内存
    uint8_t port_92 = inb(0x92);      //读取92号端口
    outb(0x92,port_92 | 0x2);       //第二位置一

    //设置全局段描述符表
    lgdt((uint32_t)gdt_table,sizeof(gdt_table));
    print_msg("GDT settings\n\r");
    //设置CR0 PE位
    print_msg("protect mode enter\n\r");
    CR0_PE();
    //清空cpu流水线
    far_jump(8,(uint32_t)protect);   
}
   
void loader_entry() 
{
    //显示加载信息
    print_msg("loading............\n\r");
    //检测内存容量
    detect_memory();
    //进入保护模式
    protect_entry();
}